package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"db2/models"
	"encoding/json"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
)

//Env HTTP服务的整环境变量
type Env struct {
	db     models.DB
	cookie sessions.Store
	tmp    map[string]*template.Template
}

func main() {
	log.SetFlags(log.LstdFlags | log.Llongfile)
	//cookie := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
	//cookie := sessions.NewCookieStore([]byte("SESSION_KEY"))

	//初始化
	cookie := sessions.NewFilesystemStore("./session/", []byte("SESSION_KEY"))
	cookie.MaxAge(60 * 5)
	db, err := models.NewDB("mysql", "root:root@tcp(127.0.0.1:3306)/test?charset=utf8")

	if err != nil {
		log.Panic(err)
	}
	env := &Env{
		db:     db,
		cookie: cookie}


	r := mux.NewRouter()
	r.Use(env.LoggingMiddleware)
	r.HandleFunc("/", env.Index).Methods("GET")
	r.HandleFunc("/login", env.LoginGet).Methods("GET")
	r.HandleFunc("/login", env.LoginPost).Methods("POST") //5.Queries("name", "{name}", "password", "{password}")
	r.HandleFunc("/logout", env.Logout).Methods("GET", "POST")

	ur := r.PathPrefix("/user").Subrouter()
	ur.HandleFunc("/{id:[0-9]+}/info", env.UserInfo).Methods("GET")
	ur.HandleFunc("/{id:[0-9]+}", env.User).Methods("GET")
	ur.Path("/all").Methods("GET").HandlerFunc(env.UserAll) //.Queries("limit","{[0-9]*?}","start","{[0-9]*?}")

	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("web/"))))
	//打印Handle列表
	r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {

		if pathTemplate, err := route.GetPathTemplate(); err == nil {
			log.Println("ROUTE:", pathTemplate)
		}
		if pathRegexp, err := route.GetPathRegexp(); err == nil {
			log.Println("Path regexp:", pathRegexp)
		}
		if queriesTemplates, err := route.GetQueriesTemplates(); err == nil {
			log.Println("Queries templates:", strings.Join(queriesTemplates, ","))
		}
		if queriesRegexps, err := route.GetQueriesRegexp(); err == nil {
			log.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
		}
		if methods, err := route.GetMethods(); err == nil {
			log.Println("Methods:", strings.Join(methods, ","))
		}
		log.Println()
		return nil
	})

	if err != nil {
		log.Println(err)
	}

	srvHttp := &http.Server{
		Addr:    ":8080",
		Handler: r,
	}
	go func() {
		srvHttp.RegisterOnShutdown(env.OnShutdown)
		if err := srvHttp.ListenAndServe(); err != nil {
			log.Println("HTTP:", err)
		}
	}()

	pool := x509.NewCertPool()
	caCrt, err := ioutil.ReadFile("ca/ca.crt")
	if err != nil {
		log.Panicln("ReadFile err:", err)
	}
	if ok := pool.AppendCertsFromPEM(caCrt); !ok {
		log.Panicln("failed to parse root certificate")
	}
	ctx:=context.WithValue(context.Background(),"env",env)
	srvHttps := &http.Server{
		Addr:    ":8081",
		Handler: r,
		TLSConfig: &tls.Config{
			ClientCAs:          pool,
			ClientAuth:         tls.RequireAndVerifyClientCert,
			InsecureSkipVerify: false,
		},
	}
	//srvHttps.ErrorLog
	go func() {
		srvHttps.RegisterOnShutdown(env.OnShutdown)
		if err := srvHttps.ListenAndServeTLS("ca/server.crt", "ca/server.key"); err != nil {
			log.Println("HTTPS", err)
		}
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	<-c
	log.Println("五秒后关闭系统")
	if err := srvHttps.Shutdown(ctx); err != nil {
		log.Println("HTTPS Shutdown", err)
	}
	if err := srvHttp.Shutdown(ctx); err != nil {
		log.Println("HTTP Shutdown", err)
	}
	<-ctx.Done()
}

func (env *Env) OnShutdown() {
	log.Println("系统关闭通知")
	log.Println("关闭数据库连接。。。")
	env.db.Close()
}

//LoggingMiddleware 中间件
// 1.检测Session是否登录
// 2.如果为HTTPS则获取客户端证书的信息。
func (env *Env) LoggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.TLS != nil && r.TLS.HandshakeComplete {
			//&& len(r.TLS.PeerCertificates) > 0 && len(r.TLS.PeerCertificates[0].Subject.Organization) > 0
			//&r.TLS.PeerCertificates[0].Subject.ToRDNSequence()
			log.Println(r.TLS.PeerCertificates[0].Subject)
			session, _ := env.cookie.Get(r, "session-name")
			session.Values["isLogin"] = false
			session.Values["name"] = r.TLS.PeerCertificates[0].Subject.Organization[0]
			session.Save(r, w)
			next.ServeHTTP(w, r)
		} else if strings.EqualFold(r.URL.Path, "/login") {
			next.ServeHTTP(w, r)
		} else {
			session, _ := env.cookie.Get(r, "session-name")
			if _, ok := session.Values["isLogin"]; ok {
				//每次请求都进行session延期
				session.Save(r, w)
				next.ServeHTTP(w, r)
			} else {
				//http.Error(w, "error!no login", 500)
				http.Redirect(w, r, "/login", 302)
			}
		}

	})
}

func (env *Env) Index(w http.ResponseWriter, r *http.Request) {
	session, _ := env.cookie.Get(r, "session-name")
	t, _ := template.ParseFiles("template/index.html")
	t.Execute(w, map[string]interface{}{"Name": session.Values["name"]}) //执行模板的merger操作
}

//LoginPost  处理登录事件
func (env *Env) Logout(w http.ResponseWriter, r *http.Request) {
	session, _ := env.cookie.Get(r, "session-name")
	session.Values["isLogin"] = false
	session.Save(r, w)
}

//LoginPost  处理登录事件
func (env *Env) LoginPost(w http.ResponseWriter, r *http.Request) {
	user := &models.User{Name: r.FormValue("name"), Passwd: r.FormValue("password")}
	//json.NewDecoder(r.Body).Decode(user)
	dao, _ := env.db.NewDAO()
	defer dao.Close()
	if isLogin, err := dao.CheckPasswd(*user); isLogin {
		session, _ := env.cookie.Get(r, "session-name")
		session.Values["isLogin"] = true
		session.Values["name"] = r.FormValue("name")
		session.Values[42] = 43
		session.Save(r, w)
		//http.Redirect(w, r, "/index.html", 302)
		t, _ := template.ParseFiles("template/login.html")
		t.Execute(w, map[string]interface{}{"Message": "登录成功", "IsLogin": true}) //执行模板的merger操作
	} else {
		if err == nil {
			t, _ := template.ParseFiles("template/login.html")
			t.Execute(w, map[string]interface{}{"Message": "登录失败，请重新登录"}) //执行模板的merger操作
		} else {
			http.Error(w, err.Error(), 500)
		}
	}

}

//LoginGet 返回登录页面
func (env *Env) LoginGet(w http.ResponseWriter, r *http.Request) {
	ctx:=r.Context()
	log.Println(ctx);

	session, _ := env.cookie.Get(r, "session-name")
	if _, ok := session.Values["isLogin"]; ok {
		w.Write([]byte("已登录"))
		return
	}
	t, _ := template.ParseFiles("template/login.html")
	t.Execute(w, map[string]interface{}{"Message": "请登录"}) //执行模板的merger操作
	return
}

//UserAll 返回全部用户
func (env *Env) UserAll(w http.ResponseWriter, r *http.Request) {
	//key := r.FormValue("key")
	//vars := mux.Vars(r)
	limit, err := strconv.Atoi(r.FormValue("limit"))
	if limit == 0 {
		limit = 10
	}
	start, err := strconv.Atoi(r.FormValue("start"))
	dao, _ := env.db.NewDAO()
	defer dao.Close()
	users, err := dao.GetAllUsers(limit, start)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	json.NewEncoder(w).Encode(map[string]interface{}{"url": r.URL.Path, "count": len(users), "result": users})
	return
}

//UserInfo 获取用户附加信息
func (env *Env) UserInfo(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	dao, _ := env.db.NewDAO()
	defer dao.Close()
	uis, err := dao.GetUserInfo(id, -1, 0)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	json.NewEncoder(w).Encode(uis)
	return
}

//User 获取用户信息
func (env *Env) User(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.ParseInt(vars["id"], 0, 64)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	dao, _ := env.db.NewDAO()
	defer dao.Close()
	if user,find, _ := dao.GetUserByID(id);find{
		json.NewEncoder(w).Encode(user)
	}else{
		http.Error(w, "用户不存在", 404)
	}
}
